home *** CD-ROM | disk | FTP | other *** search
/ Amiga Plus 1999 #2 / Amiga Plus CD - 1999 - No. 2.iso / System-Boost / Workbench / Archive / PP_v1.4 / Source / PP.C < prev    next >
C/C++ Source or Header  |  1998-11-08  |  24KB  |  1,059 lines

  1. /**************************************************************************
  2.  
  3.                         *** Powerpacker Patcher ***
  4.  
  5.                                 Version 1.4
  6.  
  7.     PP  is  a  small  utility  which enables programs to get at Powerpacked
  8. datafiles  in  a completely transparent fashion.  Any program attempting to
  9. open  a  Powerpacked datafile will receive the "uncrunched" version of that
  10. file.
  11.  
  12.     This  is  acomplished  by  patching  some  vital  DOS  functions,  thus
  13. redirecting calls to these to my own internal routines.  The overall effect
  14. is that Powerpacked datafiles appear as normal files.  You can TYPE them in
  15. a  CLI  window, or bring them directly into your favourite editor.  Another
  16. good  way  to  use  this  proggy  is  if  you  crunch your workbench icons.
  17. Workbench  will  never  know  the  difference, but it reduces the diskspace
  18. occupied  by icons by some 65-70% (usually).
  19.  
  20.     Of  course,  there is a nominal performance reduction in the DOS, which
  21. is   caused  by  the  decruncher,  but  this  doesn't  seem  too  annoying.
  22. Especially not if you use optimized (B.A.D.) disks, or increase the size of
  23. the diskbuffers (using the CLI command 'Addbuffers' or equivalent).
  24.  
  25. For [much] further info, read the DOC file.
  26.  
  27. Compiles  under  Aztec  V5.0b  using large code/large data/32 bit integers.
  28. Should do fine under Lattice, too, if you fix the #pragma's.
  29.  
  30. Shareware 1991, Copyright (C) 1991 by    Michael Berg
  31.                     Sct. Peders Gade 24A, 2th
  32.                     8900  Randers
  33.                     DENMARK
  34.  
  35. **************************************************************************/
  36.  
  37. /* That's right -- ALL of these are necessary! */
  38. #include <pragmas.h>
  39. #include <exec/execbase.h>
  40. #include <exec/nodes.h>
  41. #include <exec/lists.h>
  42. #include <exec/memory.h>
  43. #include <libraries/dos.h>
  44. #include <libraries/dosextens.h>
  45. #include <workbench/startup.h>
  46. #include <workbench/workbench.h>
  47.  
  48. /* Well, the compiler has the inline-code ability -- why not use it? */
  49. #define strlen    _BUILTIN_strlen
  50. #define strcpy    _BUILTIN_strcpy
  51.  
  52. /* loop construct */
  53. #define FOREVER for(;;)
  54.  
  55. /* Externals */
  56. extern int myPPLoadData(BPTR,UBYTE **,int *);
  57. extern struct Task *res(char *,int,int (*fptr)(),int);
  58. extern struct MsgPort *myCreatePort(char *,int);
  59. extern void myDeletePort(struct MsgPort *);
  60. extern int addfilenode(BPTR,char *,BPTR);
  61. extern struct filenode *findfilenode(BPTR);
  62. extern void calcsize(char *,short *,short *,short *,short *);
  63. extern char *stoa(short,char *);
  64. extern struct WBStartup *WBenchMsg;
  65.  
  66. /* Open()-patch related functions */
  67. extern void MakeOpen(void), RestOpen(void);
  68. extern BPTR RealOpen(char *, int);
  69. extern BPTR NewOpen(char *, int);
  70. #pragma regcall(RealOpen(d1,d2))    /* All of these PRAGMAs are EXTREMELY */
  71. #pragma regcall(NewOpen(d1,d2))        /* important !!!!!!!!!!              */
  72.  
  73. /* Close()-patch related functions */
  74. extern void MakeClose(void), RestClose(void);
  75. extern void RealClose(BPTR);
  76. extern void NewClose(BPTR);
  77. #pragma regcall(RealClose(d1))
  78. #pragma regcall(NewClose(d1))
  79.  
  80. /* Examine()-patch related functions */
  81. extern void MakeExamine(void), RestExamine(void);
  82. extern int RealExamine(BPTR, struct FileInfoBlock *);
  83. extern int NewExamine(BPTR, struct FileInfoBlock *);
  84. #pragma regcall(RealExamine(d1,d2))
  85. #pragma regcall(NewExamine(d1,d2))
  86.  
  87. /* Write()-patch related functions */
  88. extern void MakeWrite(void), RestWrite(void);
  89. extern int RealWrite(BPTR, char *, int);
  90. extern int NewWrite(BPTR, char *, int);
  91. #pragma regcall(RealWrite(d1,d2,d3))
  92. #pragma regcall(NewWrite(d1,d2,d3))
  93.  
  94. /* Define the maximum length of a filename and its path.
  95. ** 256 is, as far as I can tell, the AmigaDOS (BSTR) limit
  96. */
  97. #define MAXPATHLEN    256
  98.  
  99. /* Match tag for PowerPacked files (not encrypted) */
  100. #define PP20        (('P' << 24) + ('P' << 16) + ('2' << 8) + '0')
  101.  
  102. /* One of these for each opened, powerpacked file, which has not yet been
  103. ** closed by the Open()'er.
  104. */
  105. struct filenode
  106. {
  107.     struct MinNode mn;
  108.  
  109.     BPTR filehandle;            /* Our new file */
  110.     char *new_filename;
  111.     BPTR orig_file;
  112.     short dirty;
  113. };
  114.  
  115. /* For inter-process communication */
  116. typedef struct
  117. {
  118.     struct Message Msg;
  119.     int Result;
  120.     /* Additional parameters will live here, eventually */
  121. } MYMSG;
  122.  
  123. /* Global data */
  124. struct MinList templist;
  125. struct Window *win;
  126. struct IntuitionBase *IntuitionBase;
  127. struct IconBase *IconBase;
  128. int patched, wbmode;
  129.  
  130. USHORT opencnt,closecnt,examcnt,writecnt;
  131. USHORT pending_exit;
  132.  
  133. char temppath[MAXPATHLEN];
  134. short vcheck;
  135.  
  136. struct MsgPort *pp_port;
  137. char *pp_portname = "pp_port";
  138.  
  139. /* Misc */
  140. char *gbanner = "    Powerpacker Patcher V1.4\n\
  141.  Copyright © 1991, Michael Berg\n     Installation completed\n";
  142.  
  143. char *offbanner = "    Powerpacker Patcher V1.4\n\
  144.  Copyright © 1991, Michael Berg\n       Removal completed\n";
  145.  
  146. char *wbbanner =
  147. "         Installation trouble?\n\n\
  148. Simply double-click on my icon, or do an\n\
  149. extended  selection  on a disk or drawer\n\
  150. icon  (tells  me  where to put temporary\n\
  151. files).   Your AmigaDOS manual will tell\n\
  152. you how to do extended selections.\n";
  153.  
  154. char *clibanner =
  155. "Syntax: PP [-n] [-c] [<temppath>] ( -c and RAM: are defaults)\n";
  156.  
  157. char *wbopts =
  158. "My icon contains some TOOLTYPE values that\n\I can't make any sense \
  159. of. Select INFO from\nthe Workbench menu to fix this.\n";
  160.  
  161. char *virmsg1 =
  162. "                      *** WARNING ***\n\n The  DOS  vector  for ";
  163.  
  164. char *virmsg2 =
  165. "() has been changed by someone!\n Your machine may be subject \
  166. to a link-virus attack!\n\n I  can  no  longer  guarantee  \
  167. any kind of DOS integrity and\n protection  against  virus  \
  168. attacks.   This  is  a  one-time\n message, from now on you're \
  169. on your own...\n\n Note:   A  changed  vector  doesn't  have  \
  170. to  imply a virus\n attack. It is most likely a harmless program \
  171. which is merely\n trying  to install itself.  I suggest you consult \
  172. your virus\n checker as soon as possible.  Just to be sure.\n\n\
  173.  Press RETURN -> ";
  174.  
  175. char *defpath = "RAM:";
  176.  
  177. void wbmsg(register char *what)
  178. {
  179.     register BPTR fh;
  180.     short l,t,w,h;
  181.     char conspec[40];
  182.     char *tmp;
  183.  
  184.     calcsize(what,&l,&t,&w,&h);
  185.     strcpy(conspec,"CON:");
  186.     tmp = stoa(l,conspec+4);
  187.     *tmp++ = '/';
  188.     tmp = stoa(t,tmp);
  189.     *tmp++ = '/';
  190.     tmp = stoa(w,tmp);
  191.     *tmp++ = '/';
  192.     strcpy(stoa(h,tmp),"/Message From PP");
  193.  
  194.     if (fh = Open(conspec,MODE_NEWFILE))
  195.     {
  196.         if (IntuitionBase) WBenchToFront();
  197.         Write(fh,what,strlen(what));
  198.         Delay((3*strlen(what))/2);    /* Weird, but works okay */
  199.         Close(fh);
  200.     }
  201. }
  202.  
  203. /* Small puts() function (doesn't do a newline, though). If running from
  204. ** CLI, print the message in the CLI window. If running from WB, show the
  205. ** message in a small console window.
  206. */
  207. void Say(register char *what)
  208. {
  209.     if (wbmode)
  210.         wbmsg(what);
  211.     else
  212.     {
  213.         register BPTR out = Output();
  214.  
  215.         /* Do we still have stdout? (CLI may have been closed..) */
  216.         if (out)
  217.             Write(out,what,strlen(what));
  218.         else
  219.             wbmsg(what);
  220.     }
  221. }
  222.  
  223. void viruswarn(register char *vect)
  224. {
  225.     static short firstmsg = 1;
  226.  
  227.     /* This is an important message. Even if we are running as a CLI
  228.     ** child, we must open a window for the message to appear in
  229.     */
  230.  
  231.     if (firstmsg && vcheck)
  232.     {
  233.         register BPTR fh;
  234.         char dummy;
  235.  
  236.         firstmsg = 0;
  237.  
  238.         if (fh = RealOpen("CON:72/22/520/148/IMPORTANT MESSAGE FROM PP",MODE_NEWFILE))
  239.         {
  240.             WBenchToFront();
  241.             RealWrite(fh,virmsg1,strlen(virmsg1));
  242.             RealWrite(fh,vect,strlen(vect));
  243.             RealWrite(fh,virmsg2,strlen(virmsg2));
  244.             Read(fh,&dummy,1);
  245.             RealClose(fh);
  246.         }
  247.     }
  248. }
  249.  
  250. void cleanup()
  251. {
  252.     if (IntuitionBase) CloseLibrary(IntuitionBase);
  253.  
  254.     /* Get rid of our port */
  255.     if (pp_port)
  256.         myDeletePort(pp_port);
  257.  
  258.     /* We have to go to sleep until there are no more tasks executing
  259.     ** our code
  260.     */
  261.     while (opencnt+closecnt+examcnt+writecnt)
  262.         Delay(50);
  263.  
  264.     /* Restore the original DOS functions. You will not find these
  265.     ** functions in the sourcecode. They are generated by the assembler
  266.     ** macro DOSLibPatch -- see asmsup.c
  267.     */
  268.     if (patched)
  269.     {
  270.         Forbid();
  271.             RestOpen();
  272.             RestClose();
  273.             RestExamine();
  274.             RestWrite();
  275.         Permit();
  276.     }
  277. }
  278.  
  279. /* Universal termination code */
  280. void die(int doscode,char *errmsg)
  281. {
  282.     /* Print the (optional) error message */
  283.     if (errmsg)
  284.         Say(errmsg);
  285.  
  286.     cleanup();
  287.  
  288.     /* Finally! */
  289.     exit(doscode);
  290. }
  291.  
  292. /* Install the DOS patches */
  293. void installpatch()
  294. {
  295.     Forbid();
  296.         MakeOpen();
  297.         MakeClose();
  298.         MakeExamine();
  299.         MakeWrite();
  300.     Permit();
  301.  
  302.     patched = 1;
  303. }
  304.  
  305. /* initialize lists */
  306. void initlist()
  307. {
  308.     NewList((struct List *)&templist);
  309. }
  310.  
  311. void doport()
  312. {
  313.     if (!(pp_port = myCreatePort(pp_portname,0)))
  314.         die(30,"Couldn't create a message port\n");
  315. }
  316.  
  317. /* passargs passes messages to the 'PP' already running somewhere */
  318. void passargs(register ac, register char **av)
  319. {
  320.     MYMSG m;
  321.     register struct Process *myself;
  322.  
  323.     myself = (struct Process *)FindTask(0);
  324.  
  325.     m.Msg.mn_Node.ln_Type    = NT_MESSAGE;
  326.     m.Msg.mn_Length        = sizeof(MYMSG);
  327.     m.Msg.mn_ReplyPort    = &myself->pr_MsgPort;
  328.  
  329.     /* Passing of future parameters go here */
  330.  
  331.     /* Tell him the bad news */
  332.     PutMsg(pp_port,(struct Message *)&m);
  333.     WaitPort(&myself->pr_MsgPort);
  334.     GetMsg(&myself->pr_MsgPort);
  335.  
  336.     if (m.Result)
  337.         Say(offbanner);
  338.     else
  339.         Say("Can't remove PP just yet. Try again a little later!\n");
  340. }
  341.  
  342. void finalizepath()
  343. {
  344.     register char c;
  345.  
  346.     c = temppath[strlen(temppath)-1];
  347.     if (c != ':' && c != '/')
  348.         strcat(temppath,"/");
  349. }
  350.  
  351. void badstartup()
  352. {
  353.     die(10,wbmode? wbbanner : clibanner);
  354. }
  355.  
  356. void checkokpath()
  357. {
  358.     register BPTR lock;
  359.     register char *c;
  360.     register allok = 0;
  361.     char testdir[MAXPATHLEN];
  362.  
  363.     strcpy(testdir,temppath);
  364.     c = testdir + strlen(testdir) - 1;
  365.     if (*c == '/') *c = '\0';
  366.  
  367.     if (lock = Lock(testdir,ACCESS_READ))
  368.     {
  369.         register struct FileInfoBlock *fib;
  370.  
  371.         if (fib = AllocMem(sizeof(*fib),MEMF_CLEAR))
  372.         {
  373.             if (Examine(lock,fib))
  374.                 allok = (fib->fib_DirEntryType >= 0);
  375.  
  376.             FreeMem(fib,sizeof(*fib));
  377.         }
  378.         UnLock(lock);
  379.     }
  380.  
  381.     if (!allok)
  382.         die(10,"Could not validate your path selection\n");
  383. }
  384.  
  385. void buildfromlock(register BPTR lock, register char *name)
  386. {
  387.     register struct FileInfoBlock *fib;
  388.     register BPTR olddir,newlock;
  389.     char *fullname();
  390.  
  391.     olddir  = CurrentDir(lock);
  392.  
  393.     if (!(newlock = Lock(name,ACCESS_READ)))
  394.     {
  395.         CurrentDir(olddir);
  396.         die(10,"Could not get a lock on your specified path\n");
  397.     }
  398.  
  399.     if (fib = AllocMem(sizeof(*fib),MEMF_CLEAR))
  400.     {
  401.         register retval;
  402.  
  403.         if (retval = Examine(newlock,fib))
  404.             strcpy(temppath,fullname(newlock,fib));
  405.  
  406.         UnLock(newlock);
  407.         CurrentDir(olddir);
  408.         FreeMem(fib,sizeof(*fib));
  409.  
  410.         if (!retval)
  411.             die(10,"Cannot examine your path specification\n");
  412.     }
  413.     else
  414.     {
  415.         UnLock(newlock);
  416.         die(30,"Running low on memory!\n");
  417.     }
  418. }
  419.  
  420. badopts()
  421. {
  422.     if (wbmode)
  423.         die(30,wbopts);
  424.     else
  425.         die(30,clibanner);
  426. }
  427.  
  428. handlecliopts(register ac, register char **av)
  429. {
  430.     register short f=0;
  431.     register char *tmp;
  432.  
  433.     /* Default flags */
  434.     vcheck = 1;        /* Check for changing vectors */
  435.  
  436.     for (++av, --ac; ac; ac--, av++)
  437.     {
  438.         if (**av == '-')
  439.         {
  440.             while (*(++*av))
  441.             {
  442.                 switch(**av)
  443.                 {
  444.                     case 'n' : case 'N' :
  445.                         vcheck = 0;
  446.                         continue;
  447.  
  448.                     case 'c' : case 'C' :
  449.                         vcheck = 1;
  450.                         continue;
  451.  
  452.                     default :
  453.                         return(0);
  454.                 }
  455.             }
  456.         }
  457.         else
  458.         {
  459.             switch (f++)
  460.             {
  461.                 case 0 :
  462.                     strcpy(temppath,*av);
  463.                     break;
  464.                 default:
  465.                     return(0);
  466.             }
  467.         }
  468.     }
  469.  
  470.     if (!f)
  471.         strcpy(temppath,defpath);
  472.  
  473.     return(1);
  474. }
  475.  
  476. parseopt(char *opt)
  477. {
  478.     /* The stricmp() function is my own. See 'misc.c' */
  479.  
  480.     if (stricmp(opt,"NOCHECK"))
  481.         vcheck = 0;
  482.     else if (stricmp(opt,"CHECK"))
  483.         vcheck = 1;
  484.     else
  485.         return(0);
  486.  
  487.     return(1);
  488. }
  489.  
  490. handlewbopts()
  491. {
  492.     struct WBArg *arg = WBenchMsg->sm_ArgList;
  493.     int rv = 0;
  494.  
  495.     if (!(IconBase = OpenLibrary("icon.library",0)))
  496.         die(30,"Can't open the 'icon.library' (?!)\n");
  497.  
  498.     if (arg->wa_Lock && *arg->wa_Name)
  499.     {
  500.         char **tooltypes;
  501.         struct DiskObject *myicon;
  502.  
  503.         if (myicon = GetDiskObject(arg->wa_Name))
  504.         {
  505.             for (tooltypes=myicon->do_ToolTypes; *tooltypes; tooltypes++)
  506.                 rv = parseopt(*tooltypes);
  507.             FreeDiskObject(myicon);
  508.         }
  509.     }
  510.  
  511.     CloseLibrary(IconBase);
  512.  
  513.     return(rv);
  514. }
  515.  
  516. /* Executed when PP starts up the very first time */
  517. void doinitargs(register ac, register char **av)
  518. {
  519.     register char c;
  520.     register struct WBArg *arg;
  521.  
  522.     /* So far, the only thing you can tell PP is where to put all
  523.     ** the temporary files. This defaults to RAM: when no argument
  524.     ** is given. Workbench argument passing is fully supported.
  525.     */
  526.     if (!ac)
  527.     {
  528.          if (!handlewbopts())
  529.             badopts();
  530.  
  531.          switch (WBenchMsg->sm_NumArgs)
  532.          {
  533.              case 1 : strcpy(temppath,defpath);
  534.                  break;
  535.  
  536.             case 2 : arg = &WBenchMsg->sm_ArgList[1];
  537.                  if (arg->wa_Lock)
  538.                      buildfromlock(arg->wa_Lock,arg->wa_Name);
  539.                  else
  540.                     /* Should never happen */
  541.                      strcpy(temppath,arg->wa_Name);
  542.                  break;
  543.  
  544.             default: badstartup();
  545.          }
  546.     }
  547.     else
  548.         if (!handlecliopts(ac,av))
  549.             badopts();
  550.  
  551.      finalizepath();
  552.     checkokpath();
  553. }
  554.  
  555. void openlibs()
  556. {
  557.     if (!(IntuitionBase = OpenLibrary("intuition.library",0)))
  558.         die(30,"Missing 'intuition.library'\n");
  559. }
  560.  
  561. /* Open up everything */
  562. void openstuff(register ac, register char **av)
  563. {
  564.     openlibs();
  565.  
  566.     if (pp_port = FindPort(pp_portname))
  567.     {
  568.         /* There's already a working copy of PP running somewhere.
  569.         ** Pass the arguments along to it, and then exit.
  570.         */
  571.         passargs(ac,av);
  572.  
  573.         /* Don't let die() remove the port */
  574.         pp_port = NULL;
  575.         die(0,NULL);
  576.     }
  577.  
  578.     /* This is the first time around. Install everything */
  579.     doinitargs(ac,av);
  580.  
  581.     initlist();
  582.     installpatch();
  583.     doport();
  584. }
  585.  
  586. /* This baby builds a complete filename (including a path) from a BCPL
  587. ** pointer to a filehandle. Optimizations are most welcome. All those
  588. ** ParentDir() calls take a LONG time.
  589. */
  590. char *fullname(register BPTR lock, register struct FileInfoBlock *fib)
  591. {
  592.     static char pathandfile[MAXPATHLEN];
  593.     char tmp[MAXPATHLEN];
  594.     register BPTR parentlock, unlocklock;
  595.     register char *co;
  596.  
  597.     strcpy(pathandfile,fib->fib_FileName);
  598.  
  599.     parentlock = lock;
  600.     unlocklock = (BPTR)0;
  601.  
  602.     while (parentlock = ParentDir(parentlock))
  603.     {
  604.         if (unlocklock)
  605.             UnLock(unlocklock);
  606.  
  607.         if (patched? RealExamine(parentlock,fib) : Examine(parentlock,fib))
  608.         {
  609.             strcpy(tmp,fib->fib_FileName);
  610.             strcat(tmp,"/");
  611.             strcat(tmp,pathandfile);
  612.             strcpy(pathandfile,tmp);
  613.         }
  614.         else
  615.         {
  616.             UnLock(parentlock);
  617.             return(NULL);
  618.         }
  619.  
  620.         unlocklock = parentlock;
  621.     }
  622.  
  623.     if (unlocklock)
  624.     {
  625.         UnLock(unlocklock);
  626.  
  627.         if (co = (char *)index(pathandfile,'/'))
  628.             *co = ':';
  629.     }
  630.     else
  631.         strcat(pathandfile,":");
  632.  
  633.     /* This fixes a bug in the old RAM disk */
  634.     if (!strcmp(pathandfile,":"))
  635.         strcpy(pathandfile,"RAM:");
  636.  
  637.     return(pathandfile);
  638. }
  639.  
  640. reallyfile(register BPTR handle)
  641. {
  642.     return (!IsInteractive(handle));
  643. }
  644.  
  645. /* When somebody opens a PP file, we decrunch it into a temporary file
  646. ** and return a filehandle to that file. When the caller closes the file
  647. ** (which he thinks is the original disk file), he will really be closing
  648. ** the temporary file. This is a good chance for us to get rid of it,
  649. ** so that the temporary directory won't get crowded in time. HOWEVER!
  650. ** If the caller has written new data into the file, we have to rewrite the
  651. ** temporary file over the original (disk) file. flushout() does exactly
  652. ** that.
  653. */
  654. void flushout(register struct filenode *fn)
  655. {
  656.     register BPTR orighandle;
  657.     char buffer[2048];        /* Should suffice */
  658.     register short readlen;
  659.  
  660.     Seek(fn->filehandle, 0, -1);    /* Our file */
  661.     Seek(fn->orig_file,0,-1);    /* The original file */
  662.  
  663.     do
  664.     {
  665.         readlen = Read(fn->filehandle, buffer, 2048);
  666.         RealWrite(fn->orig_file, buffer, readlen);
  667.     }
  668.         while (readlen == 2048);
  669. }
  670.  
  671. /* Look for a powerpacker matchtag at the beginning of a file. Returns TRUE
  672. ** if the file was indeed a powerpacker datafile.
  673. */
  674. isppfile(register BPTR fh)
  675. {
  676.     int ppmatchtag;
  677.  
  678.     /* This function leaves the file position ptr. at 0 for non-PP files */
  679.     Read(fh,(char *)&ppmatchtag,sizeof(int));
  680.  
  681.     if (ppmatchtag == PP20)
  682.         return(1);
  683.     else
  684.     {
  685.         Seek(fh,0,-1);
  686.         return(0);
  687.     }
  688. }
  689.  
  690. closedel(register BPTR tempfh,register char *filnambuf)
  691. {
  692.     RealClose(tempfh);
  693.     DeleteFile(filnambuf);
  694. }
  695.  
  696. BPTR xNewOpen(register char *filename, register mode)
  697. {
  698.     UBYTE *memgot;
  699.     int filelen;
  700.     register BPTR tempfh,origfile;
  701.     register struct Task *thistask;
  702.  
  703.     origfile = tempfh = RealOpen(filename,mode);
  704.  
  705.     /* We only deal with a few of the incoming calls:
  706.     **
  707.     ** 1) Files which CAN in fact be opened
  708.     ** 2) We can't do anything about new files
  709.     ** 3) Equally, we don't care about CON: or NIL: file open requests
  710.     ** 4) We don't care about non-crunched files
  711.     */
  712.  
  713.     if
  714.     (
  715.         !tempfh               ||
  716.         mode == MODE_NEWFILE       ||
  717.         !reallyfile(tempfh)       ||
  718.         !isppfile(tempfh)
  719.     )
  720.         return(tempfh);
  721.  
  722.     /* Now, ask ppLoadData to bring in the file */
  723.     if (!myPPLoadData(tempfh,&memgot,&filelen))
  724.     {
  725.         char filnambuf[MAXPATHLEN];
  726.         register char *t, *m;
  727.  
  728.         /* Generate a name for the temporary file */
  729.         t = filename;
  730.         if (m = (char *)index(t,':')) t = m+1;
  731.         while (m = (char *)index(t,'/')) t = m+1;
  732.  
  733.         strcpy(filnambuf,temppath);
  734.         strcat(filnambuf,t);
  735.         strcat(filnambuf,".tmp");
  736.  
  737.         /* We have to ensure that the name is unique on 'temppath' */
  738.         while (tempfh = RealOpen(filnambuf,MODE_OLDFILE))
  739.         {
  740.             char *xtra = "?";
  741.  
  742.             /* Pad the name with random characters. This
  743.             ** should do the trick
  744.             */
  745.             RealClose(tempfh);
  746.             *xtra = 'A' + (rand() % 26);
  747.             strcat(filnambuf,xtra);
  748.         }
  749.  
  750.         /* Now, open the temporary file and flush data we loaded into
  751.         ** this file.
  752.         */
  753.         if (tempfh = RealOpen(filnambuf,MODE_NEWFILE))
  754.         {
  755.             register short w_err = 0;
  756.  
  757.                     /* Write the decrunched data to our new file. We
  758.             ** do this by writing a bit, then freeing a bit,
  759.             ** then writing the next bit -- and so forth.
  760.             ** When the temporary path is in RAM:, this is
  761.             ** a very good way to keep PP from using too much
  762.             ** memory at a time. (Read .DOC file for further
  763.             ** details)
  764.             */
  765.  
  766.             /* Chop it up in 10k slices
  767.             ** - and DON'T try to change that!
  768.             */
  769.             while (filelen > 10239 && !w_err)
  770.             {
  771.                 w_err = (RealWrite(tempfh,(char *)memgot,10240) != 10240);
  772.                 FreeMem(memgot,10240);
  773.                 filelen -= 10240;
  774.                 memgot  += 10240;
  775.             }
  776.  
  777.             if (w_err)
  778.             {
  779.                 closedel(tempfh,filnambuf);
  780.                 tempfh = origfile;
  781.                 goto quitout;
  782.             }
  783.  
  784.             /* Do the final odd-length slice, if any */
  785.             if (filelen)
  786.             {
  787.                 w_err = (RealWrite(tempfh,(char *)memgot,filelen) != filelen);
  788.                 FreeMem(memgot,filelen);
  789.  
  790.                 if (w_err)
  791.                 {
  792.                     closedel(tempfh,filnambuf);
  793.                     tempfh = origfile;
  794.                     goto quitout;
  795.                 }
  796.             }
  797.  
  798.             /* We need to reopen the file in "MODE_OLDFILE" */
  799.             RealClose(tempfh);
  800.             tempfh = RealOpen(filnambuf,MODE_OLDFILE);
  801.  
  802.             /* Remember that WE created that file */
  803.             if (!addfilenode(tempfh,filnambuf,origfile))
  804.             {
  805.                 /* Couln't do it. Return original filehandle */
  806.                 closedel(tempfh,filnambuf);
  807.                 tempfh = origfile;
  808.             }
  809.         }
  810.         else
  811.         {
  812.  
  813.             /* Housekeeping */
  814.             FreeMem(memgot,filelen);
  815.  
  816.             /* Return original file */
  817.             tempfh = origfile;
  818.         }
  819.     }
  820.  
  821. quitout:
  822.  
  823.     /* Return a filehandle to the file (ours or the original) */
  824.     Seek(tempfh,0,-1);
  825.     return(tempfh);
  826. }
  827.  
  828. /* This is the new Open() functions. All future calls to the DOS Open()
  829. ** function will be rerouted through here.
  830. */
  831. BPTR NewOpen(register char *filename, register mode)
  832. {
  833.     register BPTR retval;
  834.  
  835.     if (CheckOpen()) viruswarn("Open");
  836.  
  837.     if (pending_exit)
  838.         return(RealOpen(filename,mode));
  839.  
  840.     opencnt++;
  841.     retval = xNewOpen(filename,mode);
  842.     opencnt--;
  843.  
  844.     return(retval);
  845. }
  846.  
  847. int xNewWrite(register BPTR filehandle, register char *buffer, int length)
  848. {
  849.     register struct filenode *fn;
  850.     register wrtret;
  851.  
  852.     wrtret = RealWrite(filehandle, buffer, length);
  853.  
  854.     if (fn = findfilenode(filehandle))
  855.         fn->dirty = 1;
  856.  
  857.     return(wrtret);
  858. }
  859.  
  860. /* Yep! A new Write() function. We have to know if a process has updated
  861. ** the (substitute) file we created for it. If true, mark the file as
  862. ** being "dirty", so that we can later save it over the original PP file.
  863. */
  864. int NewWrite(register BPTR filehandle, register char *buffer, int length)
  865. {
  866.     register retval;
  867.  
  868.     if (CheckWrite()) viruswarn("Write");
  869.  
  870.     if (pending_exit)
  871.         return(RealWrite(filehandle,buffer,length));
  872.  
  873.     writecnt++;
  874.     retval = xNewWrite(filehandle,buffer,length);
  875.     writecnt--;
  876.  
  877.     return(retval);
  878. }
  879.  
  880. void xNewClose(register BPTR filehandle)
  881. {
  882.     register struct filenode *fn;
  883.  
  884.     if (fn = findfilenode(filehandle))
  885.     {
  886.         Forbid();
  887.         Remove((struct Node *)fn);
  888.         Permit();
  889.  
  890.         if (fn->dirty)
  891.             flushout(fn);
  892.  
  893.         RealClose(fn->orig_file);
  894.         RealClose(filehandle);
  895.         DeleteFile(fn->new_filename);
  896.  
  897.         FreeMem(fn->new_filename,strlen(fn->new_filename)+1);
  898.         FreeMem(fn,sizeof(*fn));
  899.     }
  900.     else
  901.         RealClose(filehandle);
  902. }
  903.  
  904. /* A new Close() function. It removes non-dirty, temporary files from
  905. ** 'temppath', and it keeps track of which files have to be updated back
  906. ** onto disk.
  907. */
  908. void NewClose(register BPTR filehandle)
  909. {
  910.     if (CheckClose()) viruswarn("Close");
  911.  
  912.     if (pending_exit)
  913.         RealClose(filehandle);
  914.     else
  915.     {
  916.         closecnt++;
  917.         xNewClose(filehandle);
  918.         closecnt--;
  919.     }
  920. }
  921.  
  922. int xNewExamine(BPTR lock, struct FileInfoBlock *fib)
  923. {
  924.     int decrunchinfo;
  925.     register examinereturn;
  926.     register BPTR tmpfh;
  927.     struct FileInfoBlock fib_backup;
  928.  
  929.     /* Start off by examining the lock */
  930.     if (!(examinereturn = RealExamine(lock,fib)))
  931.         return(0);
  932.  
  933.     /* If it's a directory, never mind */
  934.     if (fib->fib_DirEntryType >= 0)
  935.         return(examinereturn);
  936.  
  937.     /* The lock target was a simple file. Check to see if it's a
  938.     ** PP file
  939.     */
  940.     fib_backup = *fib;
  941.     tmpfh       = RealOpen(fullname(lock,fib),MODE_OLDFILE);
  942.     *fib       = fib_backup;
  943.  
  944.     if (!tmpfh)
  945.         return(examinereturn);
  946.  
  947.     if (isppfile(tmpfh))
  948.     {
  949.         /* It was. Examine decrunchinfo to get at the original
  950.         ** filesize, so that programs trying to allocate enough
  951.         ** memory to hold a certain file will get the correct
  952.         ** filesize (which is, of corz, size of the decrunched
  953.         ** file!)
  954.         */
  955.         Seek(tmpfh,-4,1);
  956.         Read(tmpfh,(char *)&decrunchinfo,sizeof(int));
  957.         fib->fib_Size = decrunchinfo >> 8;
  958.     }
  959.  
  960.     RealClose(tmpfh);
  961.  
  962.     return(examinereturn);
  963. }
  964.  
  965. /* A new Examine() function. Often, programs examine a file before opening
  966. ** it. This way, they can allocate just enough memory to hold the entire
  967. ** file. However, we have to correct Examine() calls to PowerPacked files,
  968. ** so that the correct amount of memory will be allocated by the caller.
  969. */
  970. int NewExamine(BPTR lock, struct FileInfoBlock *fib)
  971. {
  972.     register retval;
  973.  
  974.     if (CheckExamine()) viruswarn("Examine");
  975.  
  976.     if (pending_exit)
  977.         return(RealExamine(lock,fib));
  978.  
  979.     examcnt++;
  980.     retval = xNewExamine(lock,fib);
  981.     examcnt--;
  982.  
  983.     return(retval);
  984. }
  985.  
  986. maydie()
  987. {
  988.     Forbid();
  989.     pending_exit =     !(templist.mlh_Head->mln_Succ)
  990.             && !(opencnt | closecnt | examcnt | writecnt)
  991.             && !(CheckOpen() | CheckClose() | CheckExamine() | CheckWrite());
  992.     Permit();
  993.  
  994.     return(pending_exit);
  995. }
  996.  
  997. /* Hangaround() waits for messages to arrive at our port, and then deals
  998. ** with them. This routine could be written in a much smaller version, but
  999. ** I've written it so that it will be easy to add more 'commands'.
  1000. */
  1001. int hangaround()
  1002. {
  1003.     register MYMSG *m;
  1004.  
  1005.     pp_port->mp_SigTask = FindTask(0);
  1006.  
  1007.     FOREVER
  1008.     {
  1009.         WaitPort(pp_port);
  1010.  
  1011.         while (m = (MYMSG *)GetMsg(pp_port))
  1012.         {
  1013.             short candie;
  1014.  
  1015.             /* Eventually, some kind of actual communication
  1016.             ** will take place here. In this version, we simply
  1017.             ** quit whenever we spot an incoming message.
  1018.             */
  1019.  
  1020.             if (candie = maydie())
  1021.                 m->Result = 1;
  1022.             else
  1023.                 m->Result = 0;
  1024.  
  1025.             ReplyMsg((struct Message *)m);
  1026.  
  1027.             if (candie)
  1028.             {
  1029.                 cleanup();
  1030.                 return(0);
  1031.             }
  1032.         }
  1033.     }
  1034. }
  1035.  
  1036. /* Entry point */
  1037. void main(int argc, char *argv[])
  1038. {
  1039.     wbmode = (argc == 0);
  1040.  
  1041.     openstuff(argc,argv);
  1042.  
  1043.     if (!wbmode)
  1044.     {
  1045.         if (!res("Powerpacker Patcher",0,hangaround,4000))
  1046.             die(30,"Can't spawn background process!\n");
  1047.         else
  1048.             /* Tell the world the good news (CLI) */
  1049.             Say(gbanner);
  1050.     }
  1051.     else
  1052.     {
  1053.         /* Tell the world the good news (Workbench) */
  1054.         Say(gbanner);
  1055.  
  1056.         hangaround();
  1057.     }
  1058. }
  1059.